// src/utils/sms.js
import crypto from 'crypto';
import { RateLimiterMemory } from 'rate-limiter-flexible';
import { SMS_PROVIDERS } from '../config/sms.js';
import logger from './logger.js';
import { Client } from '@alicloud/sms-sdk';
import tencentcloud from 'tencentcloud-sdk-nodejs';
import fetch from 'node-fetch';

/**
 * SMS服务管理器
 */
class SMSService {
  constructor() {
    // 初始化速率限制器（每个手机号每分钟1条）
    this.rateLimiter = new RateLimiterMemory({
      points: 1,          // 每个时间段允许的次数
      duration: 60,       // 时间段(秒)
      keyPrefix: 'sms:'   // Redis键前缀（内存模式也适用）
    });

    // 服务商权重配置（根据成功率动态调整）
    this.providers = SMS_PROVIDERS.map(provider => ({
      ...provider,
      weight: 100,        // 初始权重
      failureCount: 0     // 连续失败次数
    }));

    // 敏感操作验证码模板
    this.templates = {
      register: code => `【${this.appName}】注册验证码：${code}，5分钟内有效`,
      login: code => `【${this.appName}】登录验证码：${code}，5分钟内有效`,
      reset: code => `【${this.appName}】密码重置验证码：${code}，请勿泄露`
    };

    // 应用名称（从环境变量获取）
    this.appName = process.env.APP_NAME || '我的应用';
  }

  /**
   * 发送短信验证码（主入口）
   * @param {string} phone 手机号
   * @param {string} type  业务类型(register/login/reset)
   * @returns {Promise<{code: string, provider: string}>}
   */
  async sendVerificationCode(phone, type) {
    // 1. 参数校验
    this.validatePhoneNumber(phone);
    if (!this.templates[type]) {
      throw new Error(`不支持的短信类型: ${type}`);
    }

    // 2. 频率限制检查
    try {
      await this.rateLimiter.consume(phone);
    } catch (rateLimiterRes) {
      const retryAfter = Math.ceil(rateLimiterRes.msBeforeNext / 1000);
      throw new Error(`操作过于频繁，请${retryAfter}秒后再试`);
    }

    // 3. 生成6位数字验证码
    const code = this.generateRandomCode(6);
    const message = this.templates[type](code);

    // 4. 选择服务商并发送
    const provider = this.selectProvider();
    try {
      await this.doSend(provider, phone, message);
      logger.info(`短信发送成功`, { phone, type, provider });
      
      return { code, provider };
    } catch (error) {
      // 服务商降级处理
      this.handleProviderFailure(provider);
      throw new Error(`短信发送失败: ${error.message}`);
    }
  }

  /**
   * 执行短信发送（支持多服务商自动切换）
   */
  async doSend(provider, phone, message) {
    switch (provider.name) {
      case 'aliyun':
        return this.sendViaAliyun(provider, phone, message);
      case 'tencent':
        return this.sendViaTencentCloud(provider, phone, message);
      case 'submail':
        return this.sendViaSubmail(provider, phone, message);
      default:
        throw new Error(`未配置短信服务商: ${provider.name}`);
    }
  }

  /**
   * 阿里云短信发送
   */
  async sendViaAliyun(provider, phone, message) {
    const client = new Client({
      accessKeyId: provider.key,
      secretAccessKey: provider.secret
    });

    const params = {
      PhoneNumbers: phone,
      SignName: provider.signName,
      TemplateCode: provider.templateCode,
      TemplateParam: JSON.stringify({ code: message.match(/\d{6}/)[0] })
    };

    const result = await client.sendSms(params);
    if (result.Code !== 'OK') {
      throw new Error(result.Message);
    }
  }

  /**
   * 腾讯云短信发送
   */
  async sendViaTencentCloud(provider, phone, message) {
    const SmsClient = tencentcloud.sms.v20210111.Client;

    const client = new SmsClient({
      credential: {
        secretId: provider.key,
        secretKey: provider.secret
      },
      region: provider.region || 'ap-guangzhou'
    });

    const result = await client.SendSms({
      PhoneNumberSet: [phone],
      SmsSdkAppId: provider.appId,
      SignName: provider.signName,
      TemplateId: provider.templateId,
      TemplateParamSet: [message.match(/\d{6}/)[0]]
    });

    if (result.SendStatusSet[0].Code !== 'Ok') {
      throw new Error(result.SendStatusSet[0].Message);
    }
  }

  /**
   * Submail短信发送
   */
  async sendViaSubmail(provider, phone, message) {
    const url = `https://api.mysubmail.com/message/send.json`;
    const params = new URLSearchParams();
    params.append('appid', provider.appId);
    params.append('to', phone);
    params.append('content', message);
    params.append('signature', provider.key);

    const response = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: params
    });

    const result = await response.json();
    if (result.status !== 'success') {
      throw new Error(result.msg || '短信发送失败');
    }
  }

  /**
   * 服务商选择算法（带权重）
   */
  selectProvider() {
    // 过滤掉连续失败超过3次的提供商
    const availableProviders = this.providers.filter(p => p.failureCount < 3);
    if (availableProviders.length === 0) {
      throw new Error('所有短信服务商暂时不可用');
    }

    // 按权重随机选择
    const totalWeight = availableProviders.reduce((sum, p) => sum + p.weight, 0);
    let random = Math.random() * totalWeight;

    for (const provider of availableProviders) {
      if (random < provider.weight) {
        return provider;
      }
      random -= provider.weight;
    }

    return availableProviders[0];
  }

  /**
   * 处理服务商失败（降低权重）
   */
  handleProviderFailure(provider) {
    const target = this.providers.find(p => p.name === provider.name);
    if (target) {
      target.failureCount++;
      target.weight = Math.max(10, target.weight - 30); // 权重最低降到10
      logger.warn(`短信服务商降级`, { 
        provider: provider.name, 
        weight: target.weight,
        failures: target.failureCount 
      });
    }
  }

  /**
   * 生成随机数字验证码
   */
  generateRandomCode(length) {
    return crypto.randomInt(0, 10 ** length)
      .toString()
      .padStart(length, '0');
  }

  /**
   * 手机号格式校验
   */
  validatePhoneNumber(phone) {
    if (!/^1[3-9]\d{9}$/.test(phone)) {
      throw new Error('无效的手机号码');
    }
  }
}

// 创建单例实例并导出
const smsService = new SMSService();
export default smsService;